iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 22
1

我們先去 backend.vue b-sidebar更新一下我們的連結,多一個編輯文章,

原本的 ArticleEditor.vue 是新增文章,現在我把檔名它改成 AddArticle.vue 了,記得善用 IDE 搜尋功能把 name 和相關引用都改過來,而重新創一個新的 ArticleEditor.vue 是用來編輯現有文章的。

在等等的範例中 ArticleEditor.vue 為父組件 AddArticle.vue 為子組件,我們會在 ArticleEditor 中使用 AddArticle。

以上都在 component 資料夾下。

<div class="px-3 py-2">
  <div>
    <router-link :to="'/backend/'+ who +'/add_article'">新增文章</router-link>
  </div>
  <div>
    <router-link :to="'/backend/'+ who +'/article_editor'">編輯文章</router-link>
  </div>
  <div>
    <router-link :to="'/backend/'+ who +'/user_info_editor'">編輯個人資料</router-link>
  </div>
  <div @click="F_signOut">
    <router-link to="#">登出</router-link>
  </div>
</div>

現在的 router.js ,有些 import() 我是用開心地哈哈,應該統一。

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Backend from '../views/Backend.vue'
import AddArticle from '../components/AddArticle.vue'
import UserInfo from '../components/UserInfo.vue'
import '../Model/FirebaseModel.js'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('../views/Login.vue')
  },
  {
    path: '/backend/:who',
    name: 'Backend',
    component: Backend,
    children: [
      {
        path: 'add_article',
        name: 'AddArticle',
        component: AddArticle
      },
      {
        path: 'user_info_editor',
        name: 'UserInfoEditor',
        component: UserInfo
      },
      {
        path: 'article_editor',
        name: 'ArticleEditor',
        component: () => import('../components/ArticleEditor.vue')
      }
    ]
  },
  {
    path: '/article/:articleId',
    name: 'ArticlePage',
    component: () => import('../components/ArticlePage.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export { router }

新創的 ArticleEditor.vue,編輯文章的部分我們把 AddArticle.vue (原 ArticleEditor) 拉進來用

<template>
  <b-row>
    <b-col cols="4">
      <label>文章列表:</label>
      <b-list-group>
        <b-list-group-item
          button
          v-for="post in posts"
          :key="post.id"
          @click="putArticle(post)"
        >
          {{ post.title }}
        </b-list-group-item>
      </b-list-group>
    </b-col>
    <b-col cols="8">
      <AddArticle
        :editTitle="editTitle"
        :editValue="editValue"
        :id="id"
      ></AddArticle>
    </b-col>
  </b-row>
</template>

<script>
import AddArticle from '../components/AddArticle.vue'
export default {
  name: 'ArticleEditor',
  data () {
    return {
      posts: [],
      editTitle: '',
      editValue: '',
      id: ''
    }
  },
  components: {
    AddArticle
  },
  created () {
    this.F_getCollectionDocsSort('posts', { where: 'createdAt', order: 'desc' }).then(docs => {
      this.posts = docs
    })
  },
  methods: {
    putArticle (articleInfo) {
      this.editTitle = articleInfo.title
      this.editValue = articleInfo.value
      this.id = articleInfo.id
    }
  }
}
</script>

ArticleEditor.vue 中與 $attrs 有關的就在於傳進去 Markdown 套件的綁定屬性

<AddArticle
  :editTitle="editTitle"
  :editValue="editValue"
  :id="id"
></AddArticle>

另外一個關鍵動作,click 時每次都傳該次渲染的 post 參數資料進去方法中,如此點選時就可以動態改變傳進去當 $attrs 的變數值:

@click="putArticle(post)"
putArticle (articleInfo) {
  this.editTitle = articleInfo.title
  this.editValue = articleInfo.value
  this.id = articleInfo.id
}

接著是 AddArticle.vue 子組件,我們要利用 $attrs 做一些事情

<template>
  <b-row>
    <b-modal id="modal-1" title="下一步?" @ok="F_updateArticle(articleData, addOrupdate , $attrs)">
      <p class="my-4">如要{{ addOrupdate }}文章請按確認</p>
    </b-modal>
    <b-col cols="12">
      <label for="input-large">文章標題:</label>
      <b-form-input id="input-large" size="lg" placeholder="請輸入文章標題" v-model="title"></b-form-input>
    </b-col>
    <b-col cols="12">
      <MarkdownPro
        @on-save="updateData"
        v-model="value"
      ></MarkdownPro>
    </b-col>
    <b-col class="mt-2"><b-button v-b-modal.modal-1 variant="primary">點擊{{ addOrupdate }}文章</b-button></b-col>
  </b-row>
</template>

<script>
import { MarkdownPro } from 'vue-meditor'
export default {
  name: 'AddArticle',
  data () {
    return {
      articleData: {},
      title: '',
      value: '',
      addOrupdate: '新增'
    }
  },
  watch: {
    $attrs: function (newVal, oldVal) {
      this.addOrupdate = '更新'
      this.title = newVal.editTitle
      this.value = newVal.editValue
    }
  },
  components: {
    MarkdownPro
  },
  methods: {
    updateData (saveEventInfo) {
      const splitter = '<!-- more -->'
      if (saveEventInfo.value.indexOf(splitter) === -1) {
        saveEventInfo.value = saveEventInfo.value.slice(0, 20) + splitter + saveEventInfo.value.slice(20)
      }

      saveEventInfo.stopOnMore = saveEventInfo.value.split(splitter)
      saveEventInfo.stopOnMore = saveEventInfo.stopOnMore[0] + '...'

      this.articleData = saveEventInfo
      this.articleData.title = this.title
      this.articleData.createdAt = new Date().getTime()

      this.F_showUser().then(res => {
        const buffer = {
          displayName: res.displayName,
          email: res.email,
          uid: res.uid,
          photoURL: res.photoURL
        }

        this.articleData.authorInfo = buffer
      })
    }
  }
}
</script>

<style lang="scss" scope>
.pageAddArticle {
  padding: 1rem;

  & > .row {
    & > div {
      margin: 1rem;;
    }
  }
}

.markdown-body {
  border: #aaa 2px solid;
  border-radius: 5px;
  height: 100%;
  padding: 1rem;
}
</style>

關鍵變化是這些地方:

為了共用函數,多傳了兩個引數進去判斷
<b-modal id="modal-1" title="下一步?" @ok="F_updateArticle(articleData, addOrupdate , $attrs)">
  <p class="my-4">如要{{ addOrupdate }}文章請按確認</p>
</b-modal>

監看 ArticleEditor.vue (父層組件) 傳進來的 tag attribute,會跑到 $attrs 裡面,這是 Day 4: 組件事件 ,提到的可用參數,不管有沒有通過 props 驗證都會被抓進來,利用 $attrs 改變 Markdown 套件上顯示的值,和我們的 BV Input。

<b-form-input id="input-large" size="lg" placeholder="請輸入文章標題" v-model="title"></b-form-input>

...

<MarkdownPro
    @on-save="updateData"
    v-model="value"
></MarkdownPro>

...

watch: {
  $attrs: function (newVal, oldVal) {
    this.addOrupdate = '更新'
    this.title = newVal.editTitle
    this.value = newVal.editValue
  }
}

最後是 F_updateArticle,判斷字串是"更新"的話就 return 更新方法,若不是就代表要上傳新文章,其中更新文章的部分就是使用傳進來的 attrs.id 取得:

F_updateArticle(data, addOrupdate, attrs) {
  console.log('觸發了F_updateArticle')
  if (addOrupdate === '更新') {
    var docRef = db.collection('posts').doc(attrs.id)
    return docRef.update(data).then(function () {
      console.log('Document successfully updated!')
    }).catch(function (error) {
      // The document probably doesn't exist.
      console.error('Error updating document: ', error)
    })
  }
  console.log('走入更新就不會到這')
  db.collection('posts').add(data).then(function (res) {
    console.log('新增文章成功')
  }).catch(res => {
    console.log('新增文章失敗')
  })
}

現在可以來測試新增,修改了,我們先新增個兩篇文章:

https://ithelp.ithome.com.tw/upload/images/20200930/20129819hn2ev1LsXa.png

接著去到編輯文章頁面可以看見剛剛新增的兩篇文章被插入了列表,點擊隨意地都可以發現值被傳到子組件 AddArticle 中了:

https://ithelp.ithome.com.tw/upload/images/20200930/20129819lgBCNrkhQX.png

修改文章後按更新確認,如果沒有給套件啟用自動存檔的話,記得 CTRL+S 手動存檔再按更新:

https://ithelp.ithome.com.tw/upload/images/20200930/20129819vdPdA6dkG0.png

回去首頁看看,編輯文章成功,而且我們預設都會以現在時間重新上傳,所以如果在上方看到舊文,就可以明確知道有更新。

https://ithelp.ithome.com.tw/upload/images/20200930/20129819CA08V6las2.png

編輯並更新現有文章就到這,其實原本是想要做的分開一點,想說另外再建一個編輯現有文章的組件,不要共用 addArticle.vue,但是想到我們專案好像還沒用到什麼父子組件傳遞資料,所以就硬著頭皮給套下去啦哈哈,整個專案其實真的滿雜亂的,而且很多東西都是應急解法...

比如說 if (addOrupdate === '更新') 這種方法老實說不是很好,假如你是個多國語系的系統,這是會有很大問題的,不過我們這只是個人使用的部落格,就不用苛求太多啦哈哈。

下一篇講啥呢,我也不知道,可能留言區功能吧...


沒事也可以逛逛我們其他團隊成員的文章啦 ~~
eien_zheng: 前端小嘍嘍的Golang學習旅程_The journey of learning Golang 系列
PollyPO技術: 前端設計轉前端工程師-JS踩坑雜記 30 天 系列
阿電: 忍住不打牌位,只要30天VueJS帶你上A牌 系列
喬依司: 實作經典 JavaScript 30 系列


上一篇
Day 21: 完善註冊流程 02 - 後台資訊編輯 + email 認證
下一篇
Day 23: 中場休息,留言功能前置準備
系列文
Vue CLI + Firebase 雲端資料庫 30天打造簡易部落格及後臺管理30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言